/* dynload.c Dynamic Loader for TinyScheme */
/* Original Copyright (c) 1999 Alexander Shendi     */
/* Modifications for NT and dl_* interface, scm_load_ext: D. Souflis */
/* Refurbished by Stephen Gildea */

/** \file dynload.c
    \brief Dynamic loader functions for TinyScheme
*/

#define _SCHEME_SOURCE
#include "dynload.h"
#include "gerb_file.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "common.h"

#ifndef MAXPATHLEN
#define MAXPATHLEN 1024
#endif

static void make_filename(const char* name, char* filename);
static void make_init_fn(const char* name, char* init_fn);

#ifdef _WIN32
#include <windows.h>
#else
typedef void* HMODULE;
typedef void (*FARPROC)();
#ifndef SUN_DL
#define SUN_DL
#endif
#include <dlfcn.h>
#endif

#ifdef _WIN32

#define PREFIX ""
#define SUFFIX ".dll"

static void
display_w32_error_msg(const char* additional_message) {
    LPVOID msg_buf;

    FormatMessage(
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, GetLastError(), 0, (LPTSTR)&msg_buf, 0, NULL
    );
    fprintf(stderr, _("scheme load-extension: %s: %s"), additional_message, (char*)msg_buf);
    LocalFree(msg_buf);
}

static HMODULE
dl_attach(const char* module) {
    HMODULE dll = LoadLibrary(module);
    if (!dll)
        display_w32_error_msg(module);
    return dll;
}

static FARPROC
dl_proc(HMODULE mo, const char* proc) {
    FARPROC procedure = GetProcAddress(mo, proc);
    if (!procedure)
        display_w32_error_msg(proc);
    return procedure;
}
#if 0
static void dl_detach(HMODULE mo) {
 (void)FreeLibrary(mo);
}
#endif
#elif defined(SUN_DL)

#include <dlfcn.h>

#define PREFIX "lib"
#define SUFFIX ".so"

static HMODULE
dl_attach(const char* module) {
    HMODULE so = dlopen(module, RTLD_LAZY);
    if (!so) {
        fprintf(stderr, _("Error loading scheme extension \"%s\": %s\n"), module, dlerror());
    }
    return so;
}

static FARPROC
dl_proc(HMODULE mo, const char* proc) {
    const char* errmsg;
    FARPROC     fp = (FARPROC)dlsym(mo, proc);
    if ((errmsg = dlerror()) == 0) {
        return fp;
    }
    fprintf(stderr, _("Error initializing scheme module \"%s\": %s\n"), proc, errmsg);
    return 0;
}
#if 0
static void dl_detach(HMODULE mo) {
 (void)dlclose(mo);
}
#endif
#endif

pointer
scm_load_ext(scheme* sc, pointer args) {
    pointer first_arg;
    pointer retval;
    char    filename[MAXPATHLEN], init_fn[MAXPATHLEN + 6];
    char*   name;
    HMODULE dll_handle;
    void (*module_init)(scheme * sc);

    if ((args != sc->NIL) && is_string((first_arg = pair_car(args)))) {
        name = string_value(first_arg);
        make_filename(name, filename);
        make_init_fn(name, init_fn);
        dll_handle = dl_attach(filename);
        if (dll_handle == 0) {
            retval = sc->F;
        } else {
            module_init = (void (*)(scheme*))dl_proc(dll_handle, init_fn);
            if (module_init != 0) {
                (*module_init)(sc);
                retval = sc->T;
            } else {
                retval = sc->F;
            }
        }
    } else {
        retval = sc->F;
    }

    return (retval);
}

static void
make_filename(const char* name, char* filename) {
    strcpy(filename, name);
    strcat(filename, SUFFIX);
}

static void
make_init_fn(const char* name, char* init_fn) {
    const char* p = strrchr(name, '/'); /*CHECK ME MINGW PATH SEPARATOR*/
    if (p == 0) {
        p = name;
    } else {
        p++;
    }
    strcpy(init_fn, "init_");
    strcat(init_fn, p);
}
